package edu.gatech.cs3240;

import java.util.ArrayList;
import java.util.Hashtable;

/**
 * Represents a terminal or nonterminal.
 * @author VRamachandran
 */
public class Token {
	
	private String name;
	
	public String toString() {
		return name;
	}
	
	/**
	 * Checks if the given Token has the same value as this Token, even if it's
	 * not the same object.
	 * @param t Other token
	 * @return Same value?
	 */
	public boolean equalValue(Token t) {
		return this.name == t.toString();
	}
	
	/**
	 * A nonterminal does not appear in the actual input string, but is a 
	 * placeholder for production rules to be applied.
	 * @author VRamachandran
	 */
	public static class Nonterminal extends Token {
		private ArrayList<Token[]> producables;
		private Hashtable<Character, Token[]> parseTable;
		private boolean producesEpsilon;
		
		public Nonterminal(String name) {
			super.name = name;
			producables = new ArrayList<Token[]>();
			parseTable = new Hashtable<Character, Token[]>();
			producesEpsilon = false;
		}
		
		/**
		 * Nonterminal's production set represents all possible production
		 * rules with this NT as the left-hand side.  Not to be confused with
		 * setExpansion(), which is based on the parse table, and determines
		 * which rule satisfies the specific character(s) to be consumed in 
		 * a given string.
		 * If this nonterminal has an epsilon transition, Terminal.EPSILON
		 * should be the first (and only) member of the array.
		 * @param prod An array of Tokens, representing the right-hand side 
		 * of the production rule.
		 */
		public void putProduction(Token[] prod) {
			producables.add(prod);
			if(prod[0] == Terminal.EPSILON)
				producesEpsilon = true;
		}
		
		/**
		 * The parse table is constructed from the First() and Follow() sets
		 * of the grammar's nonterminals, and indicates what rule should
		 * be used to consume the character.
		 * @param in
		 * @param properExpansion
		 */
		public void setExpansion(char in, Token[] properExpansion){
			parseTable.put(in, properExpansion);
		}
		
		public ArrayList<Token[]> getProductions() {
			return producables;
		}

		/**
		 * Returns the correct expansion for the given consumable token.
		 * This is equivalent to the parse table which is held in a Hashtable.
		 * As a special case, if the nonterminal has an epsilon production
		 * (i.e., Token.Terminal.EPSILON in the parseTable), then this 
		 * production is returned if and only if no other production fits the
		 * consumable.
		 * @param in A consumable character from the 
		 * @return An applicable production rule, in the form of the array of 
		 * tokens that should replace the input character.
		 */	
		public Token[] getCorrectProduction(char in){		
				// Returns null if this key doesn't expand here.  That should 
				// be handled at the caller.
			if(parseTable.get(in) == null) 
				if(producesEpsilon) 
					return new Token[0];
			return parseTable.get(in);
		}
		
	}
	
	/**
	 * A Token which actually appears in the input string.
	 * @author VRamachandran
	 */
	public static class Terminal extends Token {
		public static final Token.Terminal EPSILON = new Token.Terminal('E');
		private char value;
		
		public char getValue() {
			return value;
		}
		
		public Terminal(char c) {
			value = c;
			super.name = "" + c;
		}
	}
	
}
